var Utils = require('geoUtils');
var DragAndDropManager = require('geoDragAndDropManager');
var DragEventType = DragAndDropManager.EventType;
/**
 * 实现物体的拖拽
 */
cc.Class({
    extends: cc.Component,

    properties: {
        dragTarget: {
            default: null,
            type: cc.Node,
            tooltip: '被实施拖拽的节点，留空则使用当前节点'
        },
        dragArea: {
            default: null,
            type: cc.Node,
            tooltip: '节点拖拽区域，不设置则为被拖拽的节点'
        },
        lockTarget: {
            default: true,
            tooltip: '是否将目标锁定在拖拽点上，或者维持相对位置'
        },
        useDrop: {
            default: true,
            tooltip: '是否启用拖放系统'
        },
        dragStartEvents: {
            default: [],
            type: [cc.Component.EventHandler],
            tooltip: '当目标开始被拖拽时触发'
        },
        dragMoveEvents: {
            default: [],
            type: [cc.Component.EventHandler],
            tooltip: '当目标开始被拖拽时触发'
        },
        dragEndEvents: {
            default: [],
            type: [cc.Component.EventHandler],
            tooltip: '当目标开始被拖拽时触发'
        },
        propagateDragEvent: {
            default: false,
            tooltip: '是否传导拖拽事件（通常不需要）。'
        },
        keepStatic: {
            default: false,
            tooltip: '是否保持被拖拽物体不动，但仍可发送事件'
        },
        stopDragWhenDisabled: {
            default: true,
            tooltip: '当组件enabled=false时，是否停用拖拽功能；若此项为false，则必须显式调用stopDrag来停用拖拽'
        },
        useDraggingLayer: {
            default: false,
            tooltip: '开启后，被拖拽物体会被自动放置到拖拽层并置顶显示，拖拽层由draggingLayer属性设置。'
        },
        draggingLayer: {
            default: null,
            type: cc.Node,
            tooltip: '指定拖拽层以配合useDraggingLayer。（为空时，使用geoDragAndDropManager中配置的全局拖拽层，若也为空，则直接使用拖拽对象当前父节点作为拖拽层。）',
        },
        useLastParentWhenEndDragging:{
            default: false,
            tooltip:'结束拖拽时是否恢复到上一次的父节点。（一般配合useDraggingLayer成对使用，通常不会影响其它拖拽相关事件。也可禁用此选项，自定义拖拽结束后如何控制归属。）'
        },
        // enableDragOnLoad: true,

        ///// 隐藏属性
        _dragTarget: {
            get: function () {
                if (CC_EDITOR) return;
                //
                if (this.dragTarget != null)
                    return this.dragTarget;
                else
                    return this.node;
            },
            visible: false,
        },
        _dragArea: {
            get: function () {
                if (CC_EDITOR) return;
                //
                if (this.dragArea != null)
                    return this.dragArea;
                else
                    return this._dragTarget;
            },
            visible: false,
        },
    },

    // LIFE-CYCLE CALLBACKS:

    start() {
    },
    getDragTarget() {
        return this._dragTarget;
    },
    isInDrag() {
        return this._isInDrag;
    },
    /**
     * 开启拖拽
     */
    startDrag() {
        //
        // this.stopDrag();
        if (this._isDragEnabled === true)
            return;
        // <=====
        // this.dragTarget = this.dragTarget || this.node;
        //
        // this.dragTarget.on(cc.Node.EventType.TOUCH_START, this._onTouchStart, this);
        // this.dragTarget.on(cc.Node.EventType.TOUCH_MOVE, this._onTouchMove, this);
        // this.dragTarget.on(cc.Node.EventType.TOUCH_END, this._onTouchEnd, this);
        // this.dragTarget.on(cc.Node.EventType.TOUCH_CANCEL, this._onTouchEnd, this);
        // =====>
        var dragArea = this._dragArea;
        dragArea.on(cc.Node.EventType.TOUCH_START, this._onTouchStart, this);
        dragArea.on(cc.Node.EventType.TOUCH_MOVE, this._onTouchMove, this);
        dragArea.on(cc.Node.EventType.TOUCH_END, this._onTouchEnd, this);
        dragArea.on(cc.Node.EventType.TOUCH_CANCEL, this._onTouchEnd, this);
        //
        this._isDragEnabled = true;
    },
    /**
     * 停止拖拽
     */
    stopDrag() {
        if (this._isDragEnabled === false)
            return;
        // <=====
        // this.dragTarget = this.dragTarget || this.node;

        // this.dragTarget.off(cc.Node.EventType.TOUCH_START, this._onTouchStart, this);
        // this.dragTarget.off(cc.Node.EventType.TOUCH_MOVE, this._onTouchMove, this);
        // this.dragTarget.off(cc.Node.EventType.TOUCH_END, this._onTouchEnd, this);
        // this.dragTarget.off(cc.Node.EventType.TOUCH_CANCEL, this._onTouchEnd, this);
        // this.dragTarget = this.dragTarget || this.node;
        // =====>
        var dragArea = this._dragArea;
        dragArea.off(cc.Node.EventType.TOUCH_START, this._onTouchStart, this);
        dragArea.off(cc.Node.EventType.TOUCH_MOVE, this._onTouchMove, this);
        dragArea.off(cc.Node.EventType.TOUCH_END, this._onTouchEnd, this);
        dragArea.off(cc.Node.EventType.TOUCH_CANCEL, this._onTouchEnd, this);
        //
        this._isDragEnabled = false;
    },
    _onTouchStart(touchEvent) {
        // 若存在碰撞器，则临时开启碰撞管理更新碰撞体状态。
        // if (this._colliders.length > 0) {
        //     var cm = cc.director.getCollisionManager();
        //     this._oldCollisionManagerEnabled = cm.enabled;
        //     cm.enabled = true;
        //     // 若需要拖放，则长期开启碰撞管理，为Droppable组件准备好碰撞系统。
        //     this.scheduleOnce((() => {
        //         cm.enabled = this.useDrop;
        //     }).bind(this));
        // }
        (this._colliders.length > 0) && this._enableCollision(true);
        //
        var touch = touchEvent.touch;
        //
        var touchLocation = this._getTouchWorldLocation(touchEvent);
        //
        var isDragLocationValid = this._isDragLocationValid(touchLocation);
        var ignoreSetting = (isDragLocationValid === false);
        //
        this._handlePropagation(touchEvent, ignoreSetting);
        // 若触点不成立，就放弃后面的处理过程
        if (isDragLocationValid === false) {
            this._isInDrag = false;
            return;
        }
        // 标记处于拖拽
        this._isInDrag = true;
        //
        var dragTarget = this._dragTarget;
        var dragArea = this._dragArea;
        //
        var e = new cc.Event.EventCustom(DragEventType.DRAG_START);
        //
        e.target = dragTarget;
        e.area = dragArea;
        //
        if (this.lockTarget === false) {
            this._offset = Utils.node.getWorldPosition(dragTarget).sub(touchLocation);
        }
        //
        cc.Component.EventHandler.emitEvents(this.dragStartEvents, e, dragTarget, touchLocation, touch);
        // 向拖放系统发送事件
        if (this.useDrop === true) {
            DragAndDropManager.Singleton.emitDrag(DragAndDropManager.EventType.DRAG_START, dragTarget, dragArea, touchLocation, touch);
        }
        
        //
        this._handleDraggingLayer();
    },
    _onTouchMove(touchEvent) {
        // 若不处于拖拽状态，就放弃后面的处理。
        if (this._isInDrag !== true)
            return;
        //
        this._handlePropagation(touchEvent);
        //
        var touch = touchEvent.touch;
        //
        var touchLocation = this._getTouchWorldLocation(touchEvent);
        //
        var dragTarget = this._dragTarget;
        var dragArea = this._dragArea;
        //
        var e = new cc.Event.EventCustom(DragEventType.DRAG_MOVE);
        //
        e.target = dragTarget;
        e.area = dragArea;
        //
        if (this.keepStatic === false) {
            var dstPos = touchLocation;
            if (this.lockTarget === false) {
                dstPos = this._offset.add(touchLocation);
            }
            Utils.node.setWorldPosition(dragTarget, dstPos);
        }
        //
        cc.Component.EventHandler.emitEvents(this.dragMoveEvents, e, dragTarget, touchLocation, touch);
        // 向拖放系统发送事件
        if (this.useDrop === true) {
            DragAndDropManager.Singleton.emitDrag(DragAndDropManager.EventType.DRAG_MOVE, dragTarget, dragArea, touchLocation, touch);
        }
    },
    _onTouchEnd(touchEvent) {
        // 若存在碰撞器则恢复碰撞管理器状态
        // if (this._colliders.length > 0) {
        //     var cm = cc.director.getCollisionManager();
        //     // 先更新一下最新碰撞状态，再恢复
        //     cm.enabled = true;
        //     this.scheduleOnce((() => {
        //         //
        //         cm.enabled = this._oldCollisionManagerEnabled;
        //     }).bind(this))
        // }
        (this._colliders.length > 0) && this._enableCollision(false);
        // 若不处于拖拽状态，就放弃后面的处理。
        if (this._isInDrag !== true)
            return;
        //
        this._handlePropagation(touchEvent);


        // 处理拖拽结束归位操作
        if (this.useLastParentWhenEndDragging === true) {
            Utils.node.setParent(this.__lastParent, this.node, true);
        }
        //
        var touch = touchEvent.touch;
        // var touchLocation = touch.getLocation();
        var touchLocation = this._getTouchWorldLocation(touchEvent);

        //
        var dragTarget = this._dragTarget;
        var dragArea = this._dragArea;
        //
        var e = new cc.Event.EventCustom(DragEventType.DRAG_END);
        //
        e.target = dragTarget;
        e.area = dragArea;
        //
        cc.Component.EventHandler.emitEvents(this.dragEndEvents, e, dragTarget, touchLocation, touch);
        // 向拖放系统发送事件
        if (this.useDrop === true) {
            DragAndDropManager.Singleton.emitDrag(DragAndDropManager.EventType.DRAG_END, dragTarget, dragArea, touchLocation, touch);
        }
        // 取消标记
        this._isInDrag = false;
    },
    /**
     * 考虑相机的情况下，得到触摸的世界坐标
     * @param {cc.touchEvent} touchEvent 
     */
    _getTouchWorldLocation(touchEvent) {
        var eventNode = touchEvent.target;
        var cam = cc.Camera.findCamera(eventNode);
        var touch = touchEvent.touch;
        var location = touch.getLocation();
        location = cam.getCameraToWorldPoint(location);
        return location;
    },
    onEnable() {
        this.startDrag();
        // 收集碰撞体
        this._collectColliders();
    },
    onDisable() {
        this.stopDragWhenDisabled && this.stopDrag();
    },
    onDestroy() {
        this.stopDrag();
    },
    /**
     * 将属性复制给com
     * @param {*} com 
     */
    clone(com) {
        //com.dragTarget = this.dragTarget;
        //com.dragArea = this.dragArea;
        com.lockTarget = this.lockTarget;
        com.useDrop = this.useDrop;
        com.dragStartEvents = this.dragStartEvents;
        com.dragMoveEvents = this.dragMoveEvents;
        com.dragEndEvents = this.dragEndEvents;
        com.keepStatic = this.keepStatic;
    },
    /**
     * 处理拖拽层相关事务
     */
    _handleDraggingLayer() {
        // 是否使用拖拽层
        if (this.useDraggingLayer !== true)
            return;
        // 查找拖拽层
        var dl = this.draggingLayer || // 采用指定的拖拽层
        DragAndDropManager.Singleton.getDraggingLayer() || // 采用系统指定的拖拽层
        this._dragTarget.parent; // 采用拖拽对象的父节点作为拖拽层
        // 记录当前父节点
        this.__lastParent = this.node.parent;
        // 放入拖拽层
        Utils.node.setParent(dl, this._dragTarget, true);
        // 置顶显示
        Utils.node.showToTop(this._dragTarget);
    },
    /**
     * 处理触摸事件的传导
     * @param ignoreSetting 是否忽略传导模式设置
     */
    _handlePropagation(e, ignoreSetting) {
        if (ignoreSetting === true)
            return;
        //
        (this.propagateDragEvent !== true) && e.stopPropagation();
    },
    /**
     * 给定位置是否为合法拖拽点（是否在有效区域内）
     * @param {cc.Vec2} location 
     */
    _isDragLocationValid(location) {
        // 若无碰撞体，则采取默认判定方式
        var colliders = this._colliders;
        if (colliders.length == 0)
            return true;
        // 有碰撞体，以碰撞体判定为依据
        for (var i = 0; i < colliders.length; ++i) {
            var col = colliders[i];
            if (this._pointInCollider(location, col) == true)
                return true;
        }
        //
        return false;
    },
    /**
     * 判定点是否在碰撞体内
     * TODO：将此方法移入geoUtils
     * @param {*} point 
     * @param {*} collider 
     */
    _pointInCollider(point, collider) {
        // [debug
        collider.enabled = false;
        collider.enabled = true;
        // ]
        // 将point转变到collider的坐标下
        // var localPoint = Utils.node.getWorldToNodePosition(collider.node, point);
        if (collider instanceof cc.PolygonCollider) {
            //（貌似开启CollisionManager后，collider.world.points就正常了）
            // ret = cc.Intersection.pointInPolygon(localPoint, collider.world.points);
            return cc.Intersection.pointInPolygon(point, collider.world.points);
        }
        // else if (collider instanceof cc.BoxCollider) {
        //     ret = cc.Intersection.
        // } else if (collider instanceof cc.CircleCollider) {

        // }
        return true;
    },
    _collectColliders() {
        this._colliders = this._dragArea.getComponents(cc.Collider);
        // 默认不开启碰撞
        this._enableColliders(false);
    },
    _enableCollision(bEnable) {
        // 先开启碰撞体
        this._enableColliders(bEnable);
        //
        var cm = cc.director.getCollisionManager();

        if (bEnable == true)
            this._oldCollisionManagerEnabled = cm.enabled;
        // 只要存在碰撞体，就开启碰撞管理器一下
        cm.enabled = true;
        // 更新一下碰撞体位置，否则首次触发不到
        cm.update();
        // cm.enabled = false;

        if (bEnable == true) {
            // 若需要拖放，则长期开启碰撞管理，为Droppable组件准备好碰撞系统。
            this.scheduleOnce((() => {
                cm.enabled = this.useDrop;
                // 是否保持开启依据管理器状态
                this._enableColliders(cm.enabled);
            }).bind(this));
        } else {
            this.scheduleOnce((() => {
                // 关闭碰撞体
                this._enableColliders(false);
                // 恢复碰撞管理器状态
                cm.enabled = this._oldCollisionManagerEnabled;
            }).bind(this));
        }
    },
    /**
     * 启停碰撞器
     * @param {*} bEnable 
     */
    _enableColliders(bEnable){
        return;
        // 遗弃下面用于优化的代码以防止与外部应用冲突
        var phyEnabled = cc.director.getPhysicsManager().enabled;
        // 物理系统已经开启的情况下，就长期开启碰撞体，不再关闭。
        this._colliders.forEach(c=>c.enabled=(phyEnabled || bEnable));
    },
});